package co.edu.unal.bioingenium.kbmed.knowledge.similarity;

import co.edu.unal.bioingenium.kbmed.knowledge.KnowledgeService;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.Jaccard;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.LCH;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.Lin;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.PathLength;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.Resnick;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.SemanticSimilarity;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.metrics.WP;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author Alejandro Riveros Cruz
 */
public class SemanticSimilartyService {

    /**
     * 
     */
    public enum SemanticSimilarityFunction {

        /**
         * 
         */
        JACCARD(Jaccard.class.getSimpleName()),
        /**
         * 
         */
        LCH(LCH.class.getSimpleName()),
        /**
         * 
         */
        LIN(Lin.class.getSimpleName()),
        /**
         * 
         */
        PATH_LENGTH(PathLength.class.getSimpleName()),
        /**
         * 
         */
        RESNICK(Resnick.class.getSimpleName()),
        /**
         * 
         */
        WP(WP.class.getSimpleName());
        private String name;

        private SemanticSimilarityFunction(String semanticSimilarityName) {
            this.name = semanticSimilarityName;
        }

        /**
         * 
         * @return
         */
        public String getName() {
            return name;
        }
    }
    private Map<String, SemanticSimilarity> semanticSimilarityMap;
    private SemanticSimilarity similarityRef;
    private KnowledgeService knowledgeService;
    private static SemanticSimilartyService similartyServiceInstance;

    /**
     * 
     * @return
     */
    public static SemanticSimilartyService getInstance() {
        if (similartyServiceInstance == null) {
            similartyServiceInstance = new SemanticSimilartyService();
        }
        return similartyServiceInstance;
    }

    /**
     * 
     * @param ontologyMetaData
     */
    private SemanticSimilartyService() {
        semanticSimilarityMap = new HashMap<String, SemanticSimilarity>();
        semanticSimilarityMap.put(SemanticSimilarityFunction.JACCARD.getName(), new Jaccard());
        semanticSimilarityMap.put(SemanticSimilarityFunction.LCH.getName(), new LCH());
        semanticSimilarityMap.put(SemanticSimilarityFunction.LIN.getName(), new Lin());
        semanticSimilarityMap.put(SemanticSimilarityFunction.PATH_LENGTH.getName(), new PathLength());
        semanticSimilarityMap.put(SemanticSimilarityFunction.RESNICK.getName(), new Resnick());
        semanticSimilarityMap.put(SemanticSimilarityFunction.WP.getName(), new WP());
        knowledgeService = KnowledgeService.getInstance();
    }

    /**
     * 
     * @return
     */
    public Set<String> getSemanticSimilarityNames() {
        return semanticSimilarityMap.keySet();
    }

    /**
     * 
     * @param conceptId1
     * @param conceptId2
     * @param semanticSimilarityFunction 
     * @return
     */
    public float getSemanticSimilarityByConceptId(String conceptId1, String conceptId2, SemanticSimilarityFunction semanticSimilarityFunction) {
        similarityRef = semanticSimilarityMap.get(semanticSimilarityFunction.getName());
        String category1 = knowledgeService.getCategoryForConceptId(conceptId1);
        String category2 = knowledgeService.getCategoryForConceptId(conceptId2);
        if (category1.equalsIgnoreCase(category2)) {
            return similarityRef.getSimilarity(conceptId1, conceptId2, category1);
        } else {
            return 0;
        }
    }

    /**
     * 
     * @param descriptorId1
     * @param descriptorId2
     * @param semanticSimilarityFunction 
     * @return
     */
    public float getSemanticSimilarityByDescriptorId(String descriptorId1, String descriptorId2, SemanticSimilarityFunction semanticSimilarityFunction) {
        String conceptId1 = knowledgeService.getConceptIdForDescriptorId(descriptorId1);
        String conceptId2 = knowledgeService.getConceptIdForDescriptorId(descriptorId2);
        return getSemanticSimilarityByConceptId(conceptId1, conceptId2, semanticSimilarityFunction);
    }

    /**
     * 
     * @param conceptId 
     * @param relatedConceptIds 
     * @param semanticSimilarityFunction 
     * @return
     */
    public float[] getSemanticSimilaritysByConceptIdsArray(String conceptId, List<String> relatedConceptIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        similarityRef = semanticSimilarityMap.get(semanticSimilarityFunction.getName());
        String category1 = knowledgeService.getCategoryForConceptId(conceptId);
        String category2, relatedConceptId;
        float[] simValues = new float[relatedConceptIds.size()];
        for (int i = 0; i < relatedConceptIds.size(); i++) {
            relatedConceptId = relatedConceptIds.get(i);
            category2 = knowledgeService.getCategoryForConceptId(relatedConceptId);
            if (category1.equalsIgnoreCase(category2)) {
                simValues[i] = similarityRef.getSimilarity(conceptId, relatedConceptId, category1);
            } else {
                simValues[i] = 0;
            }
        }
        return simValues;
    }

    /**
     * 
     * @param descriptorId 
     * @param relatedDescriptorIds 
     * @param semanticSimilarityFunction 
     * @return
     */
    public float[] getSemanticSimilaritysByDesriptorIdsArray(String descriptorId, List<String> relatedDescriptorIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        String conceptId = knowledgeService.getConceptIdForDescriptorId(descriptorId);
        List<String> relatedConceptIds = knowledgeService.getConceptIdsFromDescriptorIds(relatedDescriptorIds);
        return getSemanticSimilaritysByConceptIdsArray(conceptId, relatedConceptIds, semanticSimilarityFunction);
    }

    /**
     * 
     * @param conceptId 
     * @param relatedConceptIds 
     * @param semanticSimilarityFunction 
     * @return
     */
    public List<Float> getSemanticSimilaritysByConceptIdsList(String conceptId, List<String> relatedConceptIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        similarityRef = semanticSimilarityMap.get(semanticSimilarityFunction.getName());
        String category1 = knowledgeService.getCategoryForConceptId(conceptId);
        String category2, relatedConceptId;
        List<Float> simValues = new ArrayList<Float>(relatedConceptIds.size());
        for (int i = 0; i < relatedConceptIds.size(); i++) {
            relatedConceptId = relatedConceptIds.get(i);
            category2 = knowledgeService.getCategoryForConceptId(relatedConceptId);
            if (category1.equalsIgnoreCase(category2)) {
                simValues.set(i, similarityRef.getSimilarity(conceptId, relatedConceptId, category1));
            } else {
                simValues.set(i, 0f);
            }
        }
        return simValues;

    }

    /**
     * 
     * @param descriptorId
     * @param relatedDescriptorIds
     * @param semanticSimilarityFunction
     * @return
     */
    public List<Float> getSemanticSimilaritysByDescriptorIdsList(String descriptorId, List<String> relatedDescriptorIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        String conceptId = knowledgeService.getConceptIdForDescriptorId(descriptorId);
        List<String> relatedConceptIds = knowledgeService.getConceptIdsFromDescriptorIds(relatedDescriptorIds);
        return getSemanticSimilaritysByConceptIdsList(conceptId, relatedConceptIds, semanticSimilarityFunction);
    }

    /**
     * 
     * @param conceptIds
     * @param semanticSimilarityFunction 
     * @return
     */
    public float[][] getSemanticSimilaritysByConceptIds(List<String> conceptIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        similarityRef = semanticSimilarityMap.get(semanticSimilarityFunction.getName());
        String conceptId1, conceptId2, category1, category2;
        float[][] simValues = new float[conceptIds.size()][conceptIds.size()];
        for (int i = 0; i < conceptIds.size(); i++) {
            simValues[i][i] = 1;
            conceptId1 = conceptIds.get(i);
            category1 = knowledgeService.getCategoryForConceptId(conceptId1);
            for (int j = i + 1; j < conceptIds.size(); j++) {
                conceptId2 = conceptIds.get(j);
                category2 = knowledgeService.getCategoryForConceptId(conceptId2);
                if (category1.equalsIgnoreCase(category2)) {
                    simValues[i][j] = simValues[j][i] = similarityRef.getSimilarity(conceptId1, conceptId2, category1);
                } else {
                    simValues[i][j] = 0;
                }
            }
        }
        return simValues;
    }

    /**
     * 
     * @param descriptorIds
     * @param semanticSimilarityFunction
     * @return
     */
    public float[][] getSemanticSimilaritysByDescriptorIds(List<String> descriptorIds, SemanticSimilarityFunction semanticSimilarityFunction) {
        List<String> conceptIds = knowledgeService.getConceptIdsFromDescriptorIds(descriptorIds);
        return getSemanticSimilaritysByConceptIds(conceptIds, semanticSimilarityFunction);
    }

    /**
     * 
     * @param conceptIds1 
     * @param semanticSimilarityFunction 
     * @param conceptIds2 
     * @return
     */
    public float[][] getSemanticSimilaritysByConceptIds(List<String> conceptIds1, List<String> conceptIds2, SemanticSimilarityFunction semanticSimilarityFunction) {
        similarityRef = semanticSimilarityMap.get(semanticSimilarityFunction.getName());
        String conceptId1, conceptId2, category1, category2;
        float[][] simValues = new float[conceptIds1.size()][conceptIds2.size()];
        for (int i = 0; i < conceptIds1.size(); i++) {
            conceptId1 = conceptIds1.get(i);
            category1 = knowledgeService.getCategoryForConceptId(conceptId1);
            for (int j = i; j < conceptIds2.size(); j++) {
                conceptId2 = conceptIds2.get(j);
                category2 = knowledgeService.getCategoryForConceptId(conceptId2);
                if (category1.equalsIgnoreCase(category2)) {
                    simValues[i][j] = simValues[j][i] = similarityRef.getSimilarity(conceptId1, conceptId2, category1);
                } else {
                    simValues[i][j] = 0;
                }
            }
        }
        return simValues;
    }

    /**
     * 
     * @param descriptorIds1
     * @param descriptorIds2
     * @param semanticSimilarityFunction
     * @return
     */
    public float[][] getSemanticSimilaritysByDescriptorIds(List<String> descriptorIds1, List<String> descriptorIds2, SemanticSimilarityFunction semanticSimilarityFunction) {
        List<String> conceptIds1 = knowledgeService.getConceptIdsFromDescriptorIds(descriptorIds1);
        List<String> conceptIds2 = knowledgeService.getConceptIdsFromDescriptorIds(descriptorIds2);
        return getSemanticSimilaritysByConceptIds(conceptIds1, conceptIds2, semanticSimilarityFunction);
    }
}
